using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace XMaze3D
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        private const int wallsPerCell = 5;
        private const int mazeRows = 5;
        private const int mazeCols = 5;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Effect mazeEffect;
        Matrix viewMatrix;
        Matrix projectionMatrix;
        VertexPositionColor[][] triangleVertices;

        private Texture2D[] textures;
        private int[] vertexCount;
        Maze theMaze;

        Vector3 playerPos, enemyPos;

        int modX, modY;

        GamePadState p1, p1Prev;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            theMaze = new Maze(mazeRows, mazeCols);

            triangleVertices = new VertexPositionColor[wallsPerCell][];
            for (int i = 0; i < wallsPerCell; i++)
            {
                triangleVertices[i] = new VertexPositionColor[mazeRows * mazeCols * wallsPerCell * 4];
            }

            textures = new Texture2D[wallsPerCell];

            vertexCount = new int[wallsPerCell];

            playerPos = new Vector3(0, 0, 5f);

            modX = 1;
            modY = -1;

            //int idx = 0;
            //triangleVertices[0][idx++] = new VertexPositionColor(new Vector3(-.5f, -.5f, 0), Color.White);
            //triangleVertices[0][idx++] = new VertexPositionColor(new Vector3(-.5f, .5f, 0), Color.Green);
            //triangleVertices[0][idx++] = new VertexPositionColor(new Vector3(.5f, .5f, 0), Color.Red);
            //triangleVertices[0][idx++] = new VertexPositionColor(new Vector3(-.5f, -.5f, 0), Color.White);
            //triangleVertices[0][idx++] = new VertexPositionColor(new Vector3(.5f, .5f, 0), Color.Red);
            //triangleVertices[0][idx++] = new VertexPositionColor(new Vector3(.5f, -.5f, 0), Color.Blue);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.PiOver4,
                1.333f,
                .1f,
                100f);
            viewMatrix = Matrix.CreateLookAt(
                playerPos,
                Vector3.Zero,
                Vector3.Up);

            vertexCount = new int[] { 0, 0, 0, 0, 0 };

            theMaze.Create();
            for (int i = 0; i < mazeRows; i++)
            {
                for (int j = 0; j < mazeCols; j++)
                {
                    if (theMaze.GetCell(i, j).northWallPresent)
                    {
                        DrawWall(i, j, Direction.dirNORTH);
                    }
                    if (theMaze.GetCell(i, j).eastWallPresent)
                    {
                        DrawWall(i, j, Direction.dirEAST);
                    }
                    if (theMaze.GetCell(i, j).southWallPresent)
                    {
                        DrawWall(i, j, Direction.dirSOUTH);
                    }
                    if (theMaze.GetCell(i, j).westWallPresent)
                    {
                        DrawWall(i, j, Direction.dirWEST);
                    }
                }
            }

            base.Initialize();
        }

        private void DrawWall(int row, int col, Direction direction)
        {
            Matrix rotation = Matrix.Identity;
            switch (direction)
            {
                case Direction.dirNORTH:
                    rotation = Matrix.CreateRotationY(0);
                    rotation = Matrix.Multiply(Matrix.CreateTranslation(new Vector3(0, 0, 0)), rotation);
                    break;
                case Direction.dirEAST:
                    rotation = Matrix.CreateRotationY(-MathHelper.PiOver2);
                    rotation = Matrix.Multiply(rotation, Matrix.CreateTranslation(0, 0, -row * 2));
                    break;
                case Direction.dirSOUTH:
                    return;
                    break;
                case Direction.dirWEST:
                    return;
                    break;
            }

            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                        = new VertexPositionColor(Vector3.Transform(new Vector3(-0.5f + row, -0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(-0.5f + row, 0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(0.5f + row, 0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(-0.5f + row, -0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(0.5f + row, 0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(0.5f + row, -0.5f, 0.5f - col), rotation), Color.White);

            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(0.5f + row, -0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(0.5f + row, 0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(-0.5f + row, -0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(0.5f + row, 0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(-0.5f + row, 0.5f, 0.5f - col), rotation), Color.White);
            triangleVertices[(int)direction][vertexCount[(int)direction]++]
                = new VertexPositionColor(Vector3.Transform(new Vector3(-0.5f + row, -0.5f, 0.5f - col), rotation), Color.White);
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            mazeEffect = Content.Load<Effect>("XMaze3D");

            textures[0] = Content.Load<Texture2D>("xboxControllerButtonA");
            textures[1] = Content.Load<Texture2D>("xboxControllerButtonB");
            textures[2] = Content.Load<Texture2D>("xboxControllerButtonX");
            textures[3] = Content.Load<Texture2D>("xboxControllerButtonY");
            textures[4] = Content.Load<Texture2D>("xboxControllerButtonY");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if ((GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) ||
                (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape)))
                this.Exit();

            p1 = GamePad.GetState(PlayerIndex.One);

            if (p1.IsButtonDown(Buttons.RightShoulder))
                playerPos.Y += 0.15f;

            if (p1.IsButtonDown(Buttons.A) && p1Prev.IsButtonUp(Buttons.A))
                modX *= -1;

            if (p1.IsButtonDown(Buttons.B) && p1Prev.IsButtonUp(Buttons.B))
                modY *= -1;

            // TODO: Add your update logic here

            viewMatrix = Matrix.CreateLookAt(
                playerPos,
                Vector3.Zero,
                Vector3.Up);

            Initialize();
            p1Prev = p1;
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            mazeEffect.CurrentTechnique = mazeEffect.Techniques["Ambient"];
            //mazeEffect.Parameters["World"].SetValue(Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds / 2));
            mazeEffect.Parameters["World"].SetValue(Matrix.Identity);
            mazeEffect.Parameters["View"].SetValue(viewMatrix);
            mazeEffect.Parameters["Projection"].SetValue(projectionMatrix);
            mazeEffect.Parameters["AmbientColor"].SetValue(new Vector4(1, 1, 1, 1));
            mazeEffect.Parameters["AmbientIntensity"].SetValue(.8f);

            //mazeEffect.CurrentTechnique = mazeEffect.Techniques["TransformAndTexture"];
            //mazeEffect.Parameters["UserTexture"].SetValue(northTexture);

            mazeEffect.CurrentTechnique.Passes[0].Apply();

            for (int i = 0; i < wallsPerCell; i++)
            {
                using (VertexBuffer buffer = new VertexBuffer(
                    GraphicsDevice,
                    VertexPositionColor.VertexDeclaration,
                    triangleVertices[i].Length,
                    BufferUsage.WriteOnly))
                {
                    VertexPositionColor[] tempVertices = triangleVertices[i];
                    buffer.SetData(tempVertices);
                    GraphicsDevice.SetVertexBuffer(buffer);
                }
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, triangleVertices[i].Length / 3);
            }
            base.Draw(gameTime);
        }
    }
}
